package xyz.scootaloo.kami.server.service.impl

import io.vertx.core.Future
import io.vertx.core.buffer.Buffer
import io.vertx.core.json.JsonObject
import io.vertx.ext.auth.User
import io.vertx.ext.auth.jwt.JWTAuth
import io.vertx.kotlin.core.json.jsonObjectOf
import io.vertx.kotlin.ext.auth.jwt.jwtAuthOptionsOf
import io.vertx.kotlin.ext.auth.jwtOptionsOf
import io.vertx.kotlin.ext.auth.pubSecKeyOptionsOf
import xyz.scootaloo.kami.server.model.AccessLevel
import xyz.scootaloo.kami.server.model.UserPrincipal
import xyz.scootaloo.kami.server.standard.toJsonObject
import xyz.scootaloo.kami.server.service.Constant.ID
import xyz.scootaloo.kami.server.service.Constant.ROLE
import xyz.scootaloo.kami.server.service.Constant.USERNAME
import xyz.scootaloo.kami.server.service.JwtService
import xyz.scootaloo.kami.server.verticle.vertx

/**
 * @author flutterdash@qq.com
 * @since 2022/2/6 15:58
 */
object InternalJwtServiceImpl : JwtService {

    private val provider: JWTAuth by lazy { createJwtProvider() }
    private val defJwtOptions by lazy {
        jwtOptionsOf(
            ignoreExpiration = true,
            issuer = "kami"
        )
    }

    override fun issueJWT(tokenObject: UserPrincipal): String {
        return provider.generateToken(tokenObject.toJsonObject(), defJwtOptions)
    }

    override fun authenticate(jwt: String): Future<User> {
        return provider.authenticate(
            jsonObjectOf(
                "token" to jwt,
                "ignoreExpiration" to true
            )
        )
    }

    override fun guestUserPrincipal(): JsonObject {
        return guestUserPrincipalJson
    }

    private fun createJwtProvider() = JWTAuth.create(vertx, jwtAuthOptions())
    private fun jwtAuthOptions() = jwtAuthOptionsOf(jwtOptions = defJwtOptions).addPubSecKey(pubSecKey())
    private fun pubSecKey() = pubSecKeyOptionsOf("HS256", pubSecretKeyBuf)

    private val pubSecretKeyBuf = Buffer.buffer("flutterdash@qq.com")
    private val guestUserPrincipalJson = jsonObjectOf(
        ID to -1,
        USERNAME to "guest",
        ROLE to AccessLevel.GUEST
    )
}